/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			ResMesh.d
	Description:	mesh resource
	Date:			23.11.2007 by Digited
*/
module moonglide.resources.resobjects.lowlevel.ResMesh;

import moonglide.resources.Management;


/// strings for enum
const char[][] constrendertype =
[
	"mrt_default",
	"mrt_fill",
	"mrt_fill_strip",
	"mrt_fill_quads",
	"mrt_fill_fan",
	"mrt_line",
	"mrt_line_strip",
	"mrt_line_loop",
	"mrt_point"
];

/// how the mesh will be rendered: fill = solid & textured, line = edges only, point = vertices as points only
enum EMeshRenderType
{
	mrt_default = 0,
	mrt_fill,
	mrt_fill_strip,
	mrt_fill_quads,
	mrt_fill_fan,
	mrt_line,
	mrt_line_strip,
	mrt_line_loop,
	mrt_point
}

union UResMeshData
{
	Vector4f[]		Vec4;	//	countData	==	4
	Vector3f[]		Vec3;	//	countData	==	3
	Vector2f[]		Vec2;	//	countData	==	2
	uint[]			UInt;	//	countData	==	1
};

class CResMeshField
{
	public:
		uint			vboBuff,
						countData;
		UResMeshData	data;
		bool			isStatic = true;
		bool			needToUpdate = false;

		int GetLength()
		{
			switch( countData )
			{
				case 4: return data.Vec4.length;
				case 3: return data.Vec3.length;
				case 2: return data.Vec2.length;
				case 1: return data.UInt.length;
			}
		}

		void SetLength( int len )
		{
			switch( countData )
			{
				case 4: data.Vec4.length = len;
				case 3: data.Vec3.length = len;
				case 2: data.Vec2.length = len;
				case 1: data.UInt.length = len;
			}
		}

		void* GetElement( uint indx )
		{
			switch( countData )
			{
				case 4: return data.Vec4[indx].ptr;
				case 3: return data.Vec3[indx].ptr;
				case 2: return data.Vec2[indx].ptr;
				case 1: return &data.UInt[indx];
			}
		}

		void* GetData()
		{
			switch( countData )
			{
				case 4: return data.Vec4.ptr;
				case 3: return data.Vec3.ptr;
				case 2: return data.Vec2.ptr;
				case 1: return data.UInt.ptr;
			}
		}
}

//================================================================
/// mesh resource class
//================================================================
class CResMesh : IResource
{
	protected:
		bool	_needToUpdate = false,
				_needUpload = true;

		this( char[] idname )
		{
			super( idname );

			fields["Color"]		= new CResMeshField;
			fields["Vertex"]	= new CResMeshField;
			fields["Normal"]	= new CResMeshField;
			fields["Tangent"]	= new CResMeshField;
			fields["BiNormal"]	= new CResMeshField;
			fields["TexCoord"]	= new CResMeshField;
			fields["Index"]		= new CResMeshField;

			fields["Color"].countData 		= 4;
			fields["Vertex"].countData 		= 3;
			fields["Normal"].countData 		= 3;
			fields["Tangent"].countData		= 3;
			fields["BiNormal"].countData	= 3;
			fields["TexCoord"].countData	= 2;
			fields["Index"].countData		= 1;
		}

	public:
		uint					renderType;
		EMeshRenderType			rendType = EMeshRenderType.mrt_fill;
		CResMeshField[char[]]	fields;


		void Static( bool Sta )
		{
			foreach( CResMeshField field; fields )
				field.isStatic = Sta;
		}


		void needToUpdate(bool Need)
		{
			_needToUpdate = Need;

			foreach( CResMeshField field; fields )
				field.needToUpdate = Need;
		}


		bool needToUpdate()
		{
			foreach( CResMeshField field; fields )
				if( field.needToUpdate )
					return true;

			return false;
		}


		bool needUpload() { return _needUpload; }
		void needUpload( bool need ) { _needUpload = need; }


		//============================================================
		/// opCall - main resource getter
		//============================================================
		static CResMesh opCall( char[] id, bool load = false, char[] errorResource = "" )
		{
			// check for registered ONLY in case we want to load resource
			if( load )
			{
				if( CResManager().Exists( id ))
					debug
					{
						auto tmp = cast(CResMesh)CResManager().GetResource( id );
						moonAssert( tmp !is null, "casting GetResource to CResMesh failed", __FILE__, __LINE__ );
						return tmp;
					}
					else
					{
						return cast(CResMesh)CResManager().GetResource( id );
					}

				auto res = new CResMesh( id );

				_Load( res, errorResource );
				debug moonAssert( res !is null, "loading CResMesh returned null", __FILE__, __LINE__ );
				return res;
			}

			// if we do not load, just gen unique name and create new with it.
			CResManager().GenerateUniqueName( id );
			auto res = new CResMesh( id );

			_New( res );
			return res;
		}

		// destructor
		~this()
		{
			fields = null;
		}


		debug void debugWritefields()
		{
			debugLog.trace(" ------ MESH WRITE fieldS ------");
			debugLog.trace( _id );

			debugLog.trace("field Index:");

			for( int i = 0; i < fields["Index"].GetLength; ++i )
				with( fields["Index"] )
					debugLog.trace( Format( "{}: {}", i, data.UInt[i] ) );

			debugLog.trace("field Vertex:");

			for( int i = 0; i < fields["Vertex"].GetLength; ++i )
				with( fields["Vertex"] )
					debugLog.trace( Format( "{}: {} {} {}", i, data.Vec3[i].x, data.Vec3[i].y, data.Vec3[i].z ) );

			debugLog.trace("field TexCoord:");

			for( int i = 0; i < fields["TexCoord"].GetLength; ++i )
				with( fields["TexCoord"] )
					debugLog.trace( Format( "{}: {} {}", i, data.Vec2[i].x, data.Vec2[i].y ) );

			debugLog.trace("field normal:");

			for( int i=0; i<fields["Normal"].GetLength; ++i )
				with( fields["Normal"] )
					debugLog.trace( Format( "{}: {} {} {}", i, data.Vec3[i].x, data.Vec3[i].y, data.Vec3[i].z ) );

			debugLog.trace(" ------ END MESH WRITE fieldS ------");
		}
}

unittest
{
	assert( constrendertype.length == EMeshRenderType.max + 1, "const and enum for mesh rendering type are different!" );
}
